#!/usr/bin/python
# Server program

from socket import *
from datetime import datetime
import os
import time

version='0.1.0'
#
# MISSING FEATURES
# 
# FEA1 : Multiple files and client handling
# FEA2 : md5 file identification based
# FEA3 : REGISTER checks md5 and date
# FEA4 : CONFIGURATION based server
# FEA5 : Defining and implementing command line option
# FEA6 : Objectify the application
# FEA7 : Support links and directory
# FEA8 : Support bandwitdh limitation
# FEA9 : Limit listening interface
# FEA10 : Configuration for log cleaner
# FEA11 : Referer master only
# FEA12 : rigths handling
# FEA13 : Secure udp stream
# FEA14 : Plugable file treatment
# FEA15 : temporary file write during data phase and move when committed
# FEA16 : Support for small changes only such as rigths or mtime
# FEA17 : Recursively files and directories stucture
# FEA18 : Coupling with client to be able to have cluster based synchronisation


# Set the socket parameters
host = ''
port = 12000
buf = 1024
messageTimeout=5
addr = (host,port)
ip=gethostbyaddr(gethostname())[2][0]

#misc parameter
rootDir="/tmp"
verbose=1
filesData={}
nbReq=0
retries=0

def getNowEpoch():
	return str(time.mktime(datetime.now().timetuple())).split('.')[0]

def handleError( request ):
	if verbose: print "*** ERROR;%s;%s;error for request '%s'" % (ip, getNowEpoch(), request[0])
	return ""

def addData( fileName, index, data):
	global retries
	if not int(index)%2:
		if verbose: print "*** Stop even index...."
		retries+=1
		if retries<2: return

	global filesData
	global nbReq
	filesData[int(index)]=data	
	nbReq+=1

def handleData ( request ) :
	type, index, fileName, data = request.split(';', 3)
	
	addData(fileName, index, data)	
	return ""

def handleRegister( request ):
	global nbReq
	global filesData
	global retries 
	type, fileName, size, lastModified = request.split(';')
	localFileStatus="KO"
	if os.path.exists("%s/%s" % (rootDir, fileName)): localFileStatus="OK"
	elif verbose: print "*** %s/%s doesn't exists" % (rootDir, fileName)
	
	if verbose:print "- Request : %s \n- FileName: %s\n- Size: %s\n- LastModified:%s" % ( type, fileName, size, lastModified )

	if localFileStatus == "KO" : 
		nbReq=0
		filesData={}
		retries=0
	return "ACK_REG;%s;%s;%s;%s" % (ip, getNowEpoch(), fileName, localFileStatus) 

def getFileDataSize():
	size=0
	global filesData
	for data in filesData.values():
		size+=len(data)
	return size
	
def handleCheck( request ):
	
	global nbReq
	type, fileName, size, lastModified, nbCheckReq = request.split(';')
	
	localSize = getFileDataSize()
	localStatus="OK"
	print "*** Checking file '%s' sizes:\n- client size : %d\n- server side : %d" % (fileName, int(size), localSize)
	print "*** Checking file '%s' packet numbers:\n- client : %d\n- server : %d" % (fileName, int(nbCheckReq), nbReq)
	if int(nbCheckReq) != nbReq or int(size) != localSize: 
		localStatus="KO"
		listMissing=[]
		for i in range(0, int(nbCheckReq)) :
			if not filesData.has_key(i):
				if verbose: print "*** %d items is missing" % i
				listMissing.append(str(i))
			else:	
				if verbose: print "*** %d items is OK" % i
		if verbose: print "*** Missing list : %s" % listMissing
		return "ACK_CHECK;%s;%s;%s;%s;%s" % (ip, getNowEpoch(), fileName, localStatus, ";".join(listMissing))

	return "ACK_CHECK;%s;%s;%s;%s" % (ip, getNowEpoch(), fileName, localStatus)

def  commitData2File(fileName, size, lastModified, filesData):
	fullPath="%s/%s" % (rootDir,fileName)
	f = open(fullPath, 'w')
	try:
    		for chunck in filesData.keys():
			f.write(filesData[chunck])
	finally:
    		f.close()

	os.utime(fullPath, (time.time(),int(lastModified)))

	statInfo=os.stat(fullPath)	
 	
	if verbose: print "*** SIZE: \n\t- Remote size : %d\n\t- Local Size : %d"% ( int(size), statInfo.st_size)

def handleCommit( request ) :
	type, fileName, size, lastModified = request.split(';')
	if verbose: print "*** Cool a COMMIT for '%s' :)" % fileName
	localStatus="OK"
	if int(size) != getFileDataSize(): 
		localStatus="KO"
	else : commitData2File(fileName, size, lastModified, filesData)

	return "ACK_COMMIT;%s;%s;%s;%s" % (ip, getNowEpoch(), fileName, localStatus)

def handleRequest( request ):
	handlers = {
			"REGISTER": handleRegister,
			"DATA": handleData,
			"CHECK": handleCheck,
			"COMMIT":handleCommit
		}

	if verbose: print "***	HANDLING '%s' request" % request.split(';', 1)[0]
	return handlers.get(request.split(';', 1)[0], handleError)(request)



# Create socket and bind to address
UDPSock = socket(AF_INET,SOCK_DGRAM)

UDPSock.bind(addr)

while 1:
	data,addr = UDPSock.recvfrom(buf)

	if not data:
		print "*** No client"
		break
	else:
		if verbose:print "*** Received message '", data[0:60],"...'"
	
		returnMess=handleRequest(data)		

        	if returnMess: 
			UDPSock.sendto( returnMess , addr)
			if verbose:print "*** Return message : "+ returnMess
		else: 
			if verbose:print "*** No returned data"
			
# Close socket
UDPSock.close()
